import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore';
import main from '../main';
import { AppStore, PaginateList, StorePaginate, StorePaginateListBaseParams } from '../types';
import { clearEmptyKey, objectSortToString } from '../utils/utils';

// 请求成功时传送的参数
interface SuccessParams<T> extends StorePaginateListBaseParams<T> {
  paginate: Array<T>;
  total: number;
}

// 请求失败时传的参数
interface FailureParams extends StorePaginateListBaseParams<any> {
  error: string;
}

const paginateSlice = createSlice({
  name: 'paginate',
  initialState: {} as StorePaginate<any>,
  reducers: {
    paginateRequest: <T>(state: StorePaginate<T>, action: PayloadAction<StorePaginateListBaseParams<T>>) => {
      let key = createStorePaginateListKey(action.payload);

      if (!state[key]) {
        state[key] = {
          fetching: false,
          loaded: false,
          total: 1,
          pageNum: action.payload.pageNum,
          pageSize: action.payload.pageSize,
          model: action.payload.model,
          table: action.payload.table,
          path: action.payload.path ?? 'list',
          params: action.payload.params,
        };
      }
      state[key].fetching = true;
      return state;
    },

    paginateSuccess: <T>(state: StorePaginate<T>, action: PayloadAction<SuccessParams<T>>) => {
      let key = createStorePaginateListKey(action.payload);
      let keyStart = createStorePaginateListKeyStart(action.payload);
      if (action.payload.forceRefresh) {
        let keys = Object.keys(state);
        for (let i of keys ?? []) {
          if (i.startsWith(keyStart)) {
            delete state[i];
          }
        }
      }
      if (!state[key]) {
        state[key] = {
          fetching: false,
          loaded: false,
          total: action.payload.total,
          pageNum: action.payload.pageNum,
          pageSize: action.payload.pageSize,
          model: action.payload.model,
          table: action.payload.table,
          path: action.payload.path ?? 'list',
          params: action.payload.params,
        };
      }
      state[key].fetching = false;
      state[key].loaded = true;
      state[key].error = undefined;
      state[key].total = action.payload.total;
      state[key].results = action.payload.paginate;
      return state;
    },

    paginateFailure: <T>(state: StorePaginate<T>, action: PayloadAction<FailureParams>) => {
      let key = createStorePaginateListKey(action.payload);
      if (!state[key]) {
        state[key] = {
          fetching: false,
          loaded: false,
          total: 1,
          pageNum: action.payload.pageNum,
          pageSize: action.payload.pageSize,
          model: action.payload.model,
          table: action.payload.table,
          path: action.payload.path ?? 'list',
          params: action.payload.params,
        };
      }
      state[key].fetching = false;
      state[key].loaded = true;
      state[key].error = action.payload.error;
      return state;
    },

    paginateClear: (state) => {
      return {};
    },
    paginateClearTable: (state: StorePaginate<any>, action: PayloadAction<StorePaginateListBaseParams<any>>) => {
      let keyStart = createStorePaginateListKeyStart(action.payload);
      let keys = Object.keys(state);
      for (let i of keys ?? []) {
        if (i.startsWith(keyStart)) {
          delete state[i];
        }
      }
      return state;
    },
  },
});

export const { paginateRequest, paginateSuccess, paginateFailure, paginateClear, paginateClearTable } =
  paginateSlice.actions;
export default paginateSlice.reducer;

export const paginateEffect = {
  'paginate/clear': (store: ToolkitStore<AppStore>) => store.dispatch(paginateClear()),
  'paginate/paginateRequest': async <T>(
    store: ToolkitStore<AppStore>,
    action: PayloadAction<StorePaginateListBaseParams<T>>
  ) => {
    let key = createStorePaginateListKey(action.payload);

    let pageinate: PaginateList<T> = store.getState().paginate?.[key];
    if (pageinate?.loaded && pageinate?.results && !pageinate.error && !action.payload.forceRefresh) {
      store.dispatch(paginateSuccess({ ...pageinate, paginate: pageinate.results }));
      if (action.payload.callback) {
        action.payload.callback!(200, '获取成功');
      }
    } else {
      let code: number | undefined;
      let msg: string | undefined;
      let headers: any;
      let url = `${action.payload.model}/${action.payload.table}/${action.payload.path ?? 'list'}`;
      if (url.includes('/gen/')) {
        headers = { datasource: 'master' };
      }
      try {
        // 请求接口 处理数据 并发送成功或失败action
        let res = await main.api.get(url, {
          params: clearEmptyKey(
            Object.assign({}, action.payload.params, {
              pageNum: action.payload.pageNum,
              pageSize: action.payload.pageSize,
            })
          ),
          headers,
        });
        let data = res.data;
        code = data.code;
        if (data.code === 200) {
          msg = data.msg ?? '获取成功';
          store.dispatch(
            paginateSuccess({
              total: data.total,
              pageNum: action.payload.pageNum,
              pageSize: action.payload.pageSize,
              model: action.payload.model,
              table: action.payload.table,
              path: action.payload.path ?? 'list',
              params: action.payload.params,
              paginate: data.rows,
              forceRefresh: action.payload.forceRefresh,
            })
          );
        } else {
          msg = data.msg || '获取失败';
          store.dispatch(
            paginateFailure({
              pageNum: action.payload.pageNum,
              pageSize: action.payload.pageSize,
              model: action.payload.model,
              table: action.payload.table,
              path: action.payload.path ?? 'list',
              params: action.payload.params,
              error: msg!,
            })
          );
        }
      } catch (e: any) {
        code = 500;
        msg = e.message ?? '获取失败';
        store.dispatch(
          paginateFailure({
            pageNum: action.payload.pageNum,
            pageSize: action.payload.pageSize,
            model: action.payload.model,
            table: action.payload.table,
            path: action.payload.path ?? 'list',
            params: action.payload.params,
            error: msg!,
          })
        );
      } finally {
        if (action.payload.callback) {
          action.payload.callback!(code, msg);
        }
      }
    }
  },
};

function createStorePaginateListKeyStart(payload: StorePaginateListBaseParams<any>) {
  let { model, table, path } = payload;
  return `${model}/${table}/${path ?? 'list'}`;
}

export function createStorePaginateListKey(payload: StorePaginateListBaseParams<any>) {
  let { params, model, table, path, pageNum, pageSize } = payload;
  let key = objectSortToString(clearEmptyKey(params ?? {}));
  key = `${model}/${table}/${path ?? 'list'}/pageNum:${pageNum}/pageSize:${pageSize}:${key}`;
  return key;
}
